package com.tencent.elife.utils;

import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.HashMap;

import android.graphics.Bitmap;
import android.os.Handler;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ImageView;

import com.tencent.elife.cache.CacheManager;

/**
 * @author zijianlu
 * @version 创建时间：2012-5-10
 * @Description 封装了adapterListView 中的图片异步下载和缓存,
 * 适用于ListView，GridView等控件中的图片显示
 */
public class SyncImageLoader implements OnScrollListener {
    private static final String TAG = "elife.SyncImageLoader";
    private Object mLock = new Object();

    private volatile boolean isAllowLoad = true;
    private volatile boolean isFirstLoad = true;
    private volatile int mBeginPos = 0;
    private volatile int mEndPos = 0;

    private int mRadiusPx = 0;
    private int mMaxCacheNum = 20;
    private long mCacheTime = 0;
    private int mGCIndex = 0;

    private final Handler mHandler = new Handler();

    private HashMap<Integer, SoftReference<Bitmap>> mSoftCache = null;
    private HashMap<Integer, Bitmap> mHardCache = null;
    private ArrayList<Integer> mHardCacheIndexList = null;

    private AbsListView mAbsListView = null;

    /** 构造函数 */
    public SyncImageLoader(AbsListView absListView) {
        mAbsListView = absListView;
        if (mAbsListView == null) {
            throw new RuntimeException(
                "AbsListView is null in SyncImageLoader(AbsListView absListView)");
        }

        init();
    }

    /** 构造函数 */
    public SyncImageLoader() {
        init();
    }

    private void init() {
        mSoftCache = new HashMap<Integer, SoftReference<Bitmap>>();
        mHardCache = new HashMap<Integer, Bitmap>(mMaxCacheNum);
        mHardCacheIndexList = new ArrayList<Integer>(mMaxCacheNum);
    }

    public synchronized void setLoadLimit(int beginPos, int endPos) {
        if (beginPos > endPos) {
            return;
        }
        mBeginPos = beginPos - 2;
        mEndPos = endPos + 2;
        L.D(TAG, "setLoadLimit:" + beginPos + " " + endPos);
    }

    private synchronized boolean isImgCanShow(int position) {
        if (isAllowLoad) {
            L.D(TAG, "isImgCanShow:" + position + " " + mBeginPos + " "
                + mEndPos);
            return (isFirstLoad || (position <= mEndPos && position >= mBeginPos));
        }
        return false;
    }

    public synchronized void lock() {
        isAllowLoad = false;
        isFirstLoad = false;
    }

    public void unlock() {
        isAllowLoad = true;
        synchronized (mLock) {
            mLock.notifyAll();
        }
    }

    /** 在内存中缓存的图片数目 */
    public void setMaxCacheNum(int cacheNum) {
        if (cacheNum >= 0) {
            mMaxCacheNum = cacheNum;
        }
    }

    /** 图片文件缓存的有效期，单位毫秒 */
    public void setBitmapCacheTime(long cacheTimeMills) {
        if (cacheTimeMills < 0) {
            cacheTimeMills = 0;
        }
        mCacheTime = cacheTimeMills;
    }

    /** 设置图片的圆角, 默认没有 */
    public void setCornerRadius(int radiusPx) {
        if (radiusPx < 0) {
            radiusPx = 0;
        }
        mRadiusPx = radiusPx;
    }

    /** 清空缓存图片 */
    public void clear() {
        synchronized (this) {
            isAllowLoad = false;
            mBeginPos = -1;
            mEndPos = -1;
        }

        synchronized (mLock) {
            mLock.notifyAll();
        }

        synchronized (mHardCache) {
            mSoftCache.clear();
            mHardCache.clear();
            mHardCacheIndexList.clear();
        }

        mAbsListView = null;
        System.gc();
    }

    /** 加载图片 */
    public void loadImage(final String imageUrl, final ImageView view,
        final int position) {
        view.setTag(imageUrl);

        Bitmap bmp = loadBmpFromCache(position);
        if (bmp != null) {
            view.setImageBitmap(bmp);
            return;
        }
        view.setImageDrawable(null);

        new Thread(new Runnable() {
            @Override
            public void run() {
                if (!isAllowLoad) {
                    synchronized (mLock) {
                        try {
                            mLock.wait();
                        } catch (InterruptedException e) {
                        }
                    }
                }

                if (isImgCanShow(position)) {
                    loadBmpFromNet(imageUrl, view, position);
                }
            }
        }).start();
    }

    private Bitmap loadBmpFromCache(int key) {
        synchronized (mHardCache) {
            if (mHardCache.containsKey(key)) {
                return mHardCache.get(key);
            } else if (mSoftCache.containsKey(key)) {
                SoftReference<Bitmap> softReference = mSoftCache.get(key);
                final Bitmap bmp = softReference.get();
                if (bmp != null) {
                    return bmp;
                } else {
                    mSoftCache.remove(key);
                }
            }
        }
        return null;
    }

    private void loadBmpFromNet(final String imageUrl, final ImageView view,
        final int position) {
        try {
            Bitmap rawBmp = CacheManager.fetchBitmapFromGet(imageUrl,
                mCacheTime);
            if (rawBmp != null) {
                Bitmap bmp = null;
                if (mRadiusPx > 0) {
                    bmp = AppUtils.toRoundCorner(rawBmp, mRadiusPx);
                } else {
                    bmp = rawBmp;
                }

                if (bmp != null) {
                    synchronized (mHardCache) {
                        int hardCacheSize = mHardCache.size();
                        if (hardCacheSize >= mMaxCacheNum) {
                            int oldestBmpKey = mHardCacheIndexList.get(0);
                            Bitmap oldestBmp = mHardCache.get(oldestBmpKey);

                            mHardCache.remove(oldestBmpKey);
                            mHardCacheIndexList.remove(0);

                            mSoftCache.put(oldestBmpKey,
                                new SoftReference<Bitmap>(oldestBmp));

                            mGCIndex++;
                            if (mGCIndex % 10 == 0) {
                                System.gc();
                                mGCIndex = 0;
                            }

                            oldestBmp = null;

                            int softCacheSize = mSoftCache.size();

                            L.D(TAG, "hardCache:" + hardCacheSize + " max:"
                                + mMaxCacheNum + " move first to softcahce:"
                                + softCacheSize);
                        }
                        mHardCache.put(position, bmp);
                        mHardCacheIndexList.add(position);
                    }

                    if (isImgCanShow(position)) {
                        String curUrl = (String) view.getTag();
                        if (imageUrl.equals(curUrl)) {
                            setBmp(imageUrl, view, bmp);
                        }
                    }
                    bmp = null;
                }
            }
        } catch (Exception e) {
        }
    }

    private void setBmp(final String imageUrl, final ImageView view,
        final Bitmap bmp) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                view.setImageBitmap(bmp);
            }
        });
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        switch (scrollState) {
        case AbsListView.OnScrollListener.SCROLL_STATE_FLING:
            lock();
            break;
        case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:
            if (mAbsListView != null) {
                int start = mAbsListView.getFirstVisiblePosition();
                int end = mAbsListView.getLastVisiblePosition();
                setLoadLimit(start, end);
            }
            unlock();
            break;
        case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
            lock();
            break;

        default:
            break;
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
        int visibleItemCount, int totalItemCount) {

    }
}
